// Parse.cs
//
// Andrew Bradnan 2009-2010
// andrew.bradnan@gmail.com

using System;
using System.IO;
using System.Diagnostics;

using Antlr.Runtime;
using Antlr.Runtime.Debug;
using Antlr.Runtime.Tree;

using Parser = Antlr.Runtime.Parser;
using CSharp.Util;

namespace Browse
{
	/// <summary> public partial class Parse
	/// The class to use to parse a directory of files.
	/// </summary>
	public partial class Parse
	{
        /// <summary> ParseDirectory
        ///   Depth first recursive parse of a directory.
        /// </summary>
        /// <param name="count"> Parsed successfully.</param>
        /// <param name="files"> Total files.</param>
        /// <param name="search_pattern"> File search pattern (*.cs)</param>
		public void ParseDirectory(string path, string search_pattern, ref int count, ref int files)
		{
            // use current directory if no path given
			if (path == string.Empty)
				path = ".";

            // recurse
			foreach (string subdir in Directory.GetDirectories(path))
				ParseDirectory(subdir, search_pattern, ref count, ref files);

            // parse the files
			foreach (string f in Directory.GetFiles(path, search_pattern))
			{
                // Do we pause/wait if errors are found?
                bool wait = true;
				if (search_pattern.IndexOfAny(new char[] { '*', '?' }) == -1)
					wait = false;
				if (Util.Args.IsFlagSet("-q"))		// Quick set?
					wait = false;	
				files++;

				ParseFile(f, wait, ref count);
			}	// foreach File
		}

        /** An adaptor that tells ANTLR to build CymbolAST nodes */
        public static Parse.TreeAdaptor csAdaptor = new Parse.TreeAdaptor();

        public class TreeAdaptor : CommonTreeAdaptor
        {
            [DebuggerStepThrough()]
            public override object Create(IToken token)
            {
                return new CSharpNode(token);
            }
            public override object DupNode(object t)
            {
                if (t == null)
                    return null;
                return Create(((CSharpNode)t).Token);
            }
            public override object ErrorNode(ITokenStream input, IToken start, IToken stop, RecognitionException e)
            {
                CSharpNode t = new CSharpNode(start);
                Console.WriteLine("returning error node '" + t + "' @index=" + input.Index);
                return t;
            }
        }

		/// <summary> ParseFile
		/// </summary>
		/// <param name="file_name">I expect some_rule_01.ext for a file name.  If the rule name matches a real
		/// rule I will call that method.  Otherwise, we call parse.compilation_unit(), the start of the grammar.
		/// </param>
        /// <param name="wait"> Wait on errors or keep going.</param>
        /// <param name="count"> Count of files successfully parsed.</param>
        /// <returns>parser.[rule]_return</returns>
        public CommonTreeNodeStream ParseFile(string file_name, bool wait, ref int count)
		{
            // if the -a and the Archive attribute is set on the file, then skip it
            if (ArchiveFlagIsClear(file_name, ref count))
                return null;

			Console.WriteLine("---------------");
			Console.Error.WriteLine(file_name);

            CommonTokenStream tokens = CreateLexer<PreProcessor>(file_name);
			csParser p = new csParser(tokens);
            p.TreeAdaptor = csAdaptor;
			csParser.compilation_unit_return parser_rt;
			CommonTree tree = null;

			parser_rt = p.compilation_unit();

            #region Error Checking
			tree = (CommonTree)parser_rt.Tree;
            // Check if we didn't get an AST
            // This often happens if your grammar and tree grammar don't match
            if (tree == null)
			{
                if (tokens.Count > 0)
				{
					Console.Error.WriteLine("No Tree returned from parsing! (Your rule did not parse correctly)");
				}
				else
				{
                    // the file was empty, this is not an error.
					// Clear archive attribute
					File.SetAttributes(file_name, File.GetAttributes(file_name) & ~FileAttributes.Archive);
					++count;
				}
				return null;
			}
            #endregion
            
            // Get the AST stream
			CommonTreeNodeStream nodes = new CommonTreeNodeStream(csAdaptor, tree);
			// Add the tokens for DumpNodes, otherwise there are no token names to print out.
            nodes.TokenStream = tokens;

            // Dump the tree nodes if -n is passed on the command line.
			DumpNodes(nodes);

            SymbolTable symtab = new SymbolTable(); 	// init symbol table
            Def def = new Def(nodes, symtab);       	// create Def phase
            def.Downup(tree);                          	// Do pass 1

            System.Console.WriteLine("globals: \n" + symtab.globals + "\n\n");
            System.ConsoleColor restore = System.Console.ForegroundColor;
            System.Console.ForegroundColor = ConsoleColor.Yellow;
            System.Console.WriteLine("Ref:");
            System.Console.ForegroundColor = restore;

            nodes.Reset(); // rewind AST node stream to root

            Ref @ref = new Ref(nodes);               	// create Ref phase
            @ref.Downup(tree);                          	// Do pass 2
	

			return nodes;
		}

        /// <summary> CreateLexer </summary>
		public CommonTokenStream CreateLexer<L> (string file_name)
			where L : Lexer, new()
		{
			string inputFileName = file_name;
			CommonTokenStream tokens = null;

			if (!Path.IsPathRooted(inputFileName))
				inputFileName = Path.Combine(Environment.CurrentDirectory, inputFileName);

			Console.WriteLine(inputFileName);
			ICharStream input = new ANTLRFileStream(inputFileName);
			L lex = new L();
			lex.CharStream = input;

			tokens = new CommonTokenStream(lex);
			return tokens;
		}

        /// <summary> ArchiveFlagIsClear
        /// If the command line has -a and the Archive attribute is clear, return true.
        /// We clear the archive attribute when we successfully parse a file.
        /// </summary>
        protected bool ArchiveFlagIsClear(string file_name, ref int count)
        {
			if (Util.Args.IsFlagSet("-a"))
			{
				FileAttributes attribs = File.GetAttributes(file_name);
				
				// if Archive is clear, then we parsed this already
				if ((attribs & FileAttributes.Archive) != FileAttributes.Archive)
				{
					++count;
					return true;
				}
			}
            return false;
        }
        
        /// <summary> DumpNodes
        /// The CommonTreeNodeStream has a tree in "flat form".  The UP and DOWN tokens represent the branches of the
        /// tree.  Dump these out in tree form to the console.
        /// </summary>
        private static void DumpNodes(CommonTreeNodeStream nodes)
		{
			// Dump out nodes if -n on command line
            if (Util.Args.IsFlagSet("-n"))
			{
				Console.ForegroundColor = ConsoleColor.Magenta;
				Console.WriteLine("Nodes");
				int spaces = 0;
				string str_spaces = "                                                                                       ";
				object o_prev = string.Empty;
				//for (int n = 0; n < nodes.Count; ++n)
                object o = nodes.NextElement();
                while (!nodes.IsEndOfFile(o))
				{
                    //object o = nodes.Get(n);
                    //object o = nodes[n];

					if (o.ToString() == "DOWN")
					{
						spaces += 2;
						if (o_prev.ToString() != "UP" && o_prev.ToString() != "DOWN")
							Console.Write("\r\n{0} {1}", str_spaces.Substring(0, spaces), o_prev);
					}
					else if (o.ToString() == "UP")
					{
						spaces -= 2;
						if (o_prev.ToString() != "UP" && o_prev.ToString() != "DOWN")
							Console.Write(" {0}\r\n{1}", o_prev, str_spaces.Substring(0, spaces));
					}
					else if (o_prev.ToString() != "UP" && o_prev.ToString() != "DOWN")
						Console.Write(" {0}", o_prev.ToString());

					o_prev = o;
                    o = nodes.NextElement();
                }
				if (o_prev.ToString() != "UP" && o_prev.ToString() != "DOWN")
					Console.WriteLine(" {0}", o_prev.ToString());
				Console.ResetColor();
            }
		}
	}
}
